package wiki.xsx.jg.core;

import java.math.BigDecimal;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
import java.util.List;
import java.util.Map;

/**
 * Oracle实现类
 */
public class OracleService implements DatabaseService{
    private String driverClassName;
    private String dbUrl;
    private String userName;
    private String password;

    public OracleService(String driverClassName, String dbUrl, String userName, String password) {
        this.driverClassName = driverClassName;
        this.dbUrl = dbUrl;
        this.userName = userName;
        this.password = password;
    }

    @Override
    public Map<String, Map<String, Class<?>>> getTableInfo(String tableNames) throws Exception {
        return CommonService.getTableInfo(tableNames, this);
    }

    @Override
    public void createTables(Map<String, Map<String, List<Map<String, Class<?>>>>> classInfoMap) throws Exception {
        CommonService.createTables(classInfoMap,  this);
    }

    @Override
    public String getAllTableSQL() {
        return "SELECT table_name AS name FROM USER_TABLES";
    }

    @Override
    public String getTableStructureSQL(String tableName) {
        StringBuilder builder = new StringBuilder();
        builder.append("SELECT * FROM ")
                .append(tableName)
                .append(" WHERE rownum = 1 ");
        return builder.toString();
    }

    @Override
    public String getDropTableSQL(String className) {
        StringBuilder sql = new StringBuilder();
        sql.append("DECLARE ")
            .append("   num number; ")
            .append("BEGIN ")
            .append("    SELECT COUNT(1) INTO num FROM user_tables WHERE table_name = ('").append(className).append("'); ")
            .append("    IF num > 0 THEN ")
            .append("        EXECUTE IMMEDIATE 'DROP TABLE ").append(className).append("'; ")
            .append("    END IF; ")
            .append("END");
        return sql.toString();
    }

    @Override
    public String getCreateTableSQL(String className, List<Map<String, Class<?>>> primaryList, List<Map<String, Class<?>>> isNotNullList, List<Map<String, Class<?>>> isNullList) {
        StringBuilder pk = new StringBuilder();
        StringBuilder sql = new StringBuilder();
        sql.append("CREATE TABLE ").append(className).append("(");
        for (Map<String, Class<?>> primary : primaryList){
            for (Map.Entry<String, Class<?>> entry : primary.entrySet()){
                sql.append(entry.getKey())
                    .append(getDataType(entry.getValue())).append(" NOT NULL,");

                pk.append(entry.getKey()).append(",");
            }
        }
        for (Map<String, Class<?>> isNotNull : isNotNullList){
            for (Map.Entry<String, Class<?>> entry : isNotNull.entrySet()){
                sql.append(entry.getKey())
                    .append(getDataType(entry.getValue())).append(" NOT NULL,");
            }
        }
        for (Map<String, Class<?>> isNull : isNullList){
            for (Map.Entry<String, Class<?>> entry : isNull.entrySet()){
                sql.append(entry.getKey())
                    .append(getDataType(entry.getValue())).append(",");
            }
        }
        if (pk.length()>0){
            sql.append("PRIMARY KEY (").append(pk.substring(0, pk.length()-1)).append(")");
        }else{
            sql = sql.deleteCharAt(sql.length()-1);
        }
        sql.append(")");
        return sql.toString();
    }

    @Override
    public Class<?> getTypeClass(String typeName) {
        if (typeName.matches("CHAR|VARCHAR|LONGVARCHAR")){
            return String.class;
        }else if(typeName.matches("INTEGER")) {
            return Integer.class;
        }else if(typeName.matches("BIGINT")) {
            return Long.class;
        }else if(typeName.matches("REAL")) {
            return Float.class;
        }else if(typeName.matches("FLOAT|DOUBLE")) {
            return Double.class;
        }else if(typeName.matches("NUMERIC|DECIMAL")) {
            return BigDecimal.class;
        }else if(typeName.matches("DATE|TIME|TIMESTAMP")) {
            return Date.class;
        }else if(typeName.matches("SMALLINT")) {
            return Short.class;
        }else if(typeName.matches("TINYINT")) {
            return Byte.class;
        }else if(typeName.matches("BIT")) {
            return Boolean.class;
        }else if(typeName.matches("BLOB")) {
            return Blob.class;
        }else if(typeName.matches("CLOB")) {
            return Clob.class;
        }else if(typeName.matches("BINARY|VARBINARY|LONGVARBINARY")) {
            return byte[].class;
        }else{
            return Object.class;
        }
    }

    @Override
    public String getDataType(Class<?> type) {
        if (type.getSimpleName().equalsIgnoreCase("String")){
            return "VARCHAR(255)";
        }else if(
            type.getSimpleName().equalsIgnoreCase("Integer")||
            type.getSimpleName().equalsIgnoreCase("Long")||
            type.getSimpleName().equalsIgnoreCase("Float")||
            type.getSimpleName().equalsIgnoreCase("Double")||
            type.getSimpleName().equalsIgnoreCase("BigDecimal")||
            type.getSimpleName().equalsIgnoreCase("Boolean")||
            type.getSimpleName().equalsIgnoreCase("Byte")||
            type.getSimpleName().equalsIgnoreCase("Short")
        ){
            return "NUMBER";
        }else if(
            type.getSimpleName().equalsIgnoreCase("Date")||
            type.getSimpleName().equalsIgnoreCase("Time")
        ){
            return "Date";
        }else if(type.getSimpleName().equalsIgnoreCase("Timestamp")){
            return "TIMESTAMP";
        }else if(type.getSimpleName().equalsIgnoreCase("Blob")){
            return "BLOB";
        }else if(type.getSimpleName().equalsIgnoreCase("Clob")){
            return "CLOB";
        }else if(type.getSimpleName().equalsIgnoreCase("byte[]")){
            return "RAW";
        }else{
            return "VARCHAR(255)";
        }
    }

    @Override
    public String getDBName() {
        if (this.dbUrl.contains("?")){
            return this.dbUrl.substring(this.dbUrl.lastIndexOf("/")+1, this.dbUrl.indexOf("?"));
        }else{
            return this.dbUrl.substring(this.dbUrl.lastIndexOf("/")+1);
        }
    }

    @Override
    public String getDriverClassName() {
        return this.driverClassName;
    }

    @Override
    public String getDbUrl() {
        return this.dbUrl;
    }

    @Override
    public String getDbUserName() {
        return this.userName;
    }

    @Override
    public String getDbPassword() {
        return this.password;
    }
}
